﻿using UnityEngine;
using System.Collections;

public class Aim
{
	public delegate void VoidDelegate();
	public VoidDelegate OnAimingStart;
	public VoidDelegate OnAimingComplete;

	private Trajactory m_line1;
	private Trajactory m_line2;
	private Trajactory m_line3;

	private float m_deltaTime;
	private float m_gravity;
	private PhysicsManager m_physicsManager;
	private BallProperties m_ballProperties;

	public class SphereCastInfo
	{
		public GameObject HitObject { get; private set; }
		public Vector3 Normal { get; private set; }
		public Vector3 AimingBallPos { get; private set; }
		public float HitDistance { get; private set; }

		public SphereCastInfo (Vector3 cueBallPos, float radius, Vector3 direction)
		{
			RaycastHit hit;
			if (Physics.SphereCast(cueBallPos, radius, direction, out hit))
			{
				HitObject = hit.collider.gameObject;
				Normal = hit.normal;
				AimingBallPos = hit.point + radius * hit.normal;
				HitDistance = hit.distance;
			}
			else
				Debug.LogError("SphereCast do not hit!");
		}
	}

	public Aim ()
	{
		m_line1 = new Trajactory();
		m_line2 = new Trajactory();
		m_line3 = new Trajactory();
		m_deltaTime = Time.fixedDeltaTime;
		m_gravity = Physics.gravity.y;
		m_physicsManager = GameObject.Find("Physics").GetComponent<PhysicsManager>();
		m_ballProperties = m_physicsManager.TheBallProperties;
	}

	public void ResetLines ()
	{
		m_line1.Reset();
		m_line2.Reset();
		m_line3.Reset();
	}

	public void ToAim (Transform cueBallTrans, Vector3 direction, float power, Vector3 position)
	{
		if (OnAimingStart != null)
			OnAimingStart();

		SphereCastInfo sphereCastInfo;
		m_line1 = AimingLine1(cueBallTrans, direction, power, position, out sphereCastInfo);
		m_line2 = AimingLine2(sphereCastInfo);
		m_line3 = AimingLine3();

		if (OnAimingComplete != null)
			OnAimingComplete();
	}

	public void GetAim (out Trajactory line1, out Trajactory line2, out Trajactory line3)
	{
		line1 = m_line1;
		line2 = m_line2;
		line3 = m_line3;
	}

	private Trajactory AimingLine1 (Transform cueBallTrans, Vector3 direction, float power, Vector3 position, out SphereCastInfo sphereCastInfo)
	{
		sphereCastInfo = new SphereCastInfo(cueBallTrans.position, cueBallTrans.lossyScale.x * 0.5f, direction);

		Trajactory line1 = new Trajactory();

		return line1;
	}

	private void CueBallCollision ()
	{

	}

	private Trajactory AimingLine2 (SphereCastInfo hit)
	{
		Trajactory line2 = new Trajactory();
		return line2;
	}

	private Trajactory AimingLine3 ()
	{
		Trajactory line3 = new Trajactory();
		return line3;
	}

	private Trajactory AimingLineSimulation (Vector3 initPos, Quaternion initRot, Vector3 initVel, Vector3 initAngVel, float radius,
	                                   float lineLength, float minVel = 0f, float minAngVel = 0f, float maxIterationCount = float.MaxValue)
	{
		Vector3 pos = initPos;
		Quaternion rot = initRot;
		Vector3 vel = initVel;
		Vector3 angVel = initAngVel;
		float pathLength = 0f;
		int iterationCount = 0;

		Trajactory line = new Trajactory ();
		do
		{
			line.PositionList.Add (pos);
			line.RotationList.Add (rot);
			line.VelocityList.Add (vel);
			line.AngularVelocityList.Add (angVel);

			Vector3 contactVel = vel + Vector3.Cross(angVel, radius * Vector3.down);
			vel = - m_deltaTime * m_gravity * m_ballProperties.DynamicFriction * contactVel.normalized 
				+ (m_ballProperties.Drag * m_deltaTime / m_ballProperties.Mass + 1) * vel;

			angVel = - m_deltaTime * m_ballProperties.Mass / m_ballProperties.Inertia * m_gravity * m_ballProperties.DynamicFriction * m_ballProperties.Radius * (Vector3.Cross(contactVel, Vector3.down).normalized)
				+ ( -m_ballProperties.AngDrag * m_deltaTime / m_ballProperties.Inertia + 1 ) * angVel;

			pos += vel * m_deltaTime;

			// rot = ...

			iterationCount++;
		}
		while (pathLength < lineLength && iterationCount < maxIterationCount && vel.magnitude >= minVel && angVel.magnitude >= minAngVel);

		if (vel.magnitude < minVel || angVel.magnitude < minAngVel)
		{
		
		}

		//// Fix the last transform and (angular) velocity

		return line;
	}
}
